using UnityEngine;

/// <summary>
/// 视口检测辅助类
/// </summary>
public class UIReuseViewportDetection
{
    readonly Vector3[] _corners = new Vector3[4];
    /// <summary> 上一次内容位置 </summary>
    Vector2 _prevPosition = Vector2.zero;
    /// <summary> 上一次的内容边界 </summary>
    Bounds _prevContentBounds;
    /// <summary> 上一次的视口边界 </summary>
    Bounds _prevViewportBounds;

    public RectTransform viewport;
    public RectTransform content;
    /// <summary> 内容边界 </summary>
    public Bounds contentBounds;
    /// <summary> 视口边界 </summary>
    public Bounds viewportBounds;
    
    public UIReuseViewportDetection(RectTransform viewport, RectTransform content)
    {
        this.viewport = viewport;
        this.content = content;

        TryUpdate();
    }

    public void ResizeBounds()
    {
        var size = viewport.rect.size;
        //size *= new Vector2(1f, 1.5f);
        viewportBounds = new Bounds(viewport.rect.center, size);
        contentBounds = GetBounds();
    }

    Bounds GetBounds()
    {
        content.GetWorldCorners(_corners);
        var viewWorldToLocalMatrix = viewport.worldToLocalMatrix;
        return InternalGetBounds(_corners, ref viewWorldToLocalMatrix);
    }

    void UpdateBounds()
    {
        ResizeBounds();

        Vector3 contentSize = contentBounds.size;
        Vector3 contentPos = contentBounds.center;
        var contentPivot = content.pivot;
        AdjustBounds(ref viewportBounds, ref contentPivot, ref contentSize, ref contentPos);
        contentBounds.size = contentSize;
        contentBounds.center = contentPos;
    }

    void UpdatePrevData()
    {
        _prevPosition = content.anchoredPosition;
        _prevViewportBounds = viewportBounds;
        _prevContentBounds = contentBounds;
    }

    public bool TryUpdate()
    {
        if (!content)
            return false;

        UpdateBounds();

        if (viewportBounds != _prevViewportBounds || contentBounds != _prevContentBounds ||
            content.anchoredPosition != _prevPosition)
        {
            UpdatePrevData();
            return true;
        }
        return false;
    }

    public Bounds GetVirtualBounds()
    {
        return viewportBounds;
    }




    static Bounds InternalGetBounds(Vector3[] corners, ref Matrix4x4 viewWorldToLocalMatrix)
    {
        var vMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
        var vMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);

        for (int j = 0; j < 4; j++)
        {
            Vector3 v = viewWorldToLocalMatrix.MultiplyPoint3x4(corners[j]);
            vMin = Vector3.Min(v, vMin);
            vMax = Vector3.Max(v, vMax);
        }

        var bounds = new Bounds(vMin, Vector3.zero);
        bounds.Encapsulate(vMax);
        return bounds;
    }

    static void AdjustBounds(ref Bounds viewBounds, ref Vector2 contentPivot, ref Vector3 contentSize, ref Vector3 contentPos)
    {
        Vector3 excess = viewBounds.size - contentSize;
        if (excess.x > 0)
        {
            contentPos.x -= excess.x * (contentPivot.x - 0.5f);
            contentSize.x = viewBounds.size.x;
        }
        if (excess.y > 0)
        {
            contentPos.y -= excess.y * (contentPivot.y - 0.5f);
            contentSize.y = viewBounds.size.y;
        }
    }
}

